﻿//SecurityChangeSerializer.cs
//Copyright (c) 2013 StockSharp LLC, all rights reserved.
//This code module is part of StockSharp library.
//This code is licensed under the GNU GENERAL PUBLIC LICENSE Version 3.
//See the file License.txt for the license details.
//More info on: http://stocksharp.com

namespace StockSharp.Algo.Storages
{
	using System;
	using System.Collections.Generic;
	using System.IO;
	using System.Linq;

	using Ecng.Collections;
	using Ecng.Common;
	using Ecng.Serialization;

	using StockSharp.BusinessEntities;

	class SecurityMetaInfo : MetaInfo<SecurityMetaInfo>
	{
		public SecurityMetaInfo(DateTime date)
			: base(date)
		{
			Price = new RefPair<decimal, decimal>();
			ImpliedVolatility = new RefPair<decimal, decimal>();
			HistoricalVolatility = new RefPair<decimal, decimal>();
			TheorPrice = new RefPair<decimal, decimal>();
			MinStepPrice = new RefPair<decimal, decimal>();
			Delta = new RefPair<decimal, decimal>();
			Gamma = new RefPair<decimal, decimal>();
			Vega = new RefPair<decimal, decimal>();
			Theta = new RefPair<decimal, decimal>();
			MarginBuy = new RefPair<decimal, decimal>();
			MarginSell = new RefPair<decimal, decimal>();
		}

		public RefPair<decimal, decimal> Price { get; private set; }
		public RefPair<decimal, decimal> ImpliedVolatility { get; private set; }
		public RefPair<decimal, decimal> HistoricalVolatility { get; private set; }
		public RefPair<decimal, decimal> TheorPrice { get; private set; }
		public RefPair<decimal, decimal> MinStepPrice { get; private set; }
		public RefPair<decimal, decimal> Delta { get; private set; }
		public RefPair<decimal, decimal> Gamma { get; private set; }
		public RefPair<decimal, decimal> Vega { get; private set; }
		public RefPair<decimal, decimal> Theta { get; private set; }
		public RefPair<decimal, decimal> MarginBuy { get; private set; }
		public RefPair<decimal, decimal> MarginSell { get; private set; }

		public override void Write(Stream stream)
		{
			base.Write(stream);

			Write(stream, Price);
			Write(stream, ImpliedVolatility);
			Write(stream, TheorPrice);
			Write(stream, MinStepPrice);

			if (Version >= MarketDataVersions.Version41)
			{
				Write(stream, HistoricalVolatility);

				Write(stream, Delta);
				Write(stream, Gamma);
				Write(stream, Vega);
				Write(stream, Theta);

				Write(stream, MarginBuy);
				Write(stream, MarginSell);
			}
		}

		public override void Read(Stream stream)
		{
			base.Read(stream);

			Price = ReadInfo(stream);
			ImpliedVolatility = ReadInfo(stream);
			TheorPrice = ReadInfo(stream);
			MinStepPrice = ReadInfo(stream);

			if (Version >= MarketDataVersions.Version41)
			{
				HistoricalVolatility = ReadInfo(stream);

				Delta = ReadInfo(stream);
				Gamma = ReadInfo(stream);
				Vega = ReadInfo(stream);
				Theta = ReadInfo(stream);

				MarginBuy = ReadInfo(stream);
				MarginSell = ReadInfo(stream);
			}
		}

		private static void Write(Stream stream, RefPair<decimal, decimal> info)
		{
			stream.Write(info.First);
			stream.Write(info.Second);
		}

		private static RefPair<decimal, decimal> ReadInfo(Stream stream)
		{
			return new RefPair<decimal, decimal>(stream.Read<decimal>(), stream.Read<decimal>());
		}

		protected override void CopyFrom(SecurityMetaInfo src)
		{
			base.CopyFrom(src);

			Price = Clone(src.Price);
			ImpliedVolatility = Clone(src.ImpliedVolatility);
			TheorPrice = Clone(src.TheorPrice);
			MinStepPrice = Clone(src.MinStepPrice);
			HistoricalVolatility = Clone(src.HistoricalVolatility);
			Delta = Clone(src.Delta);
			Gamma = Clone(src.Gamma);
			Vega = Clone(src.Vega);
			Theta = Clone(src.Theta);
			MarginBuy = Clone(src.MarginBuy);
			MarginSell = Clone(src.MarginSell);
		}

		private static RefPair<decimal, decimal> Clone(RefPair<decimal, decimal> info)
		{
			return new RefPair<decimal, decimal>(info.First, info.Second);
		}
	}

	class SecurityChangeSerializer : MarketDataSerializer<SecurityChange, SecurityMetaInfo>
	{
		public SecurityChangeSerializer(Security security)
			: base(security, 50)
		{
		}

		protected override void OnSave(List<bool> bits, IEnumerable<SecurityChange> changes, SecurityMetaInfo metaInfo)
		{
			bits.SerializeInt(changes.Count());

			foreach (var change in changes)
			{
				metaInfo.LastTime = bits.SerializeTime(change.Time, metaInfo.LastTime, "изменения");

				bits.SerializeInt((int)change.Type);

				switch (change.Type)
				{
					case SecurityChangeTypes.OpenPrice:
					case SecurityChangeTypes.HighPrice:
					case SecurityChangeTypes.LowPrice:
					case SecurityChangeTypes.ClosePrice:
					case SecurityChangeTypes.MinPrice:
					case SecurityChangeTypes.MaxPrice:
					{
						SerializePrice(bits, metaInfo, (decimal)change.Value);
						break;
					}
					case SecurityChangeTypes.BidsCount:
					case SecurityChangeTypes.BidsVolume:
					case SecurityChangeTypes.AsksCount:
					case SecurityChangeTypes.AsksVolume:
					case SecurityChangeTypes.OpenInterest:
					{
						bits.SerializeVolume((decimal)change.Value);
						break;
					}
					case SecurityChangeTypes.ImpliedVolatility:
					{
						SerializeChange(bits, metaInfo.ImpliedVolatility, (decimal)change.Value);
						break;
					}
					case SecurityChangeTypes.HistoricalVolatility:
					{
						SerializeChange(bits, metaInfo.HistoricalVolatility, (decimal)change.Value);
						break;
					}
					case SecurityChangeTypes.TheorPrice:
					{
						SerializeChange(bits, metaInfo.TheorPrice, (decimal)change.Value);
						break;
					}
					case SecurityChangeTypes.Delta:
					{
						SerializeChange(bits, metaInfo.Delta, (decimal)change.Value);
						break;
					}
					case SecurityChangeTypes.Gamma:
					{
						SerializeChange(bits, metaInfo.Gamma, (decimal)change.Value);
						break;
					}
					case SecurityChangeTypes.Vega:
					{
						SerializeChange(bits, metaInfo.Vega, (decimal)change.Value);
						break;
					}
					case SecurityChangeTypes.Theta:
					{
						SerializeChange(bits, metaInfo.Theta, (decimal)change.Value);
						break;
					}
					case SecurityChangeTypes.MarginBuy:
					{
						SerializeChange(bits, metaInfo.MarginBuy, (decimal)change.Value);
						break;
					}
					case SecurityChangeTypes.MarginSell:
					{
						SerializeChange(bits, metaInfo.MarginSell, (decimal)change.Value);
						break;
					}
					case SecurityChangeTypes.MinStepSize:
					{
						//нет необходимости хранить шаг цены, т.к. он есть в metaInfo
						break;
					}
					case SecurityChangeTypes.MinStepPrice:
					{
						SerializeChange(bits, metaInfo.MinStepPrice, (decimal)change.Value);
						break;
					}
					case SecurityChangeTypes.LastTrade:
					{
						var trade = (Trade)change.Value;

						SerializePrice(bits, metaInfo, trade.Price);
						bits.SerializeVolume(trade.Volume);
						bits.SerializeTradeDirection(trade.OrderDirection);

						break;
					}
					case SecurityChangeTypes.BestBid:
					case SecurityChangeTypes.BestAsk:
					{
						var quote = (Quote)change.Value;

						SerializePrice(bits, metaInfo, quote.Price);
						bits.SerializeVolume(quote.Volume);

						break;
					}
					default:
						throw new ArgumentOutOfRangeException("changes", change.Type, "Неизвестное изменение инструмента.");
				}
			}
		}

		public override SecurityChange MoveNext(MarketDataEnumerator<SecurityChange, SecurityMetaInfo> enumerator)
		{
			var reader = enumerator.Reader;
			var metaInfo = enumerator.MetaInfo;

			metaInfo.FirstTime = reader.DeserializeTime(enumerator.Date, metaInfo.FirstTime);

			var type = (SecurityChangeTypes)reader.DeserializeInt();

			object value;

			switch (type)
			{
				case SecurityChangeTypes.OpenPrice:
				case SecurityChangeTypes.HighPrice:
				case SecurityChangeTypes.LowPrice:
				case SecurityChangeTypes.ClosePrice:
				case SecurityChangeTypes.MinPrice:
				case SecurityChangeTypes.MaxPrice:
				{
					metaInfo.Price.First = reader.DeserializePrice(metaInfo.Price.First, metaInfo.MinStepSize);
					value = metaInfo.Price.First;
					break;
				}
				case SecurityChangeTypes.BidsCount:
				case SecurityChangeTypes.BidsVolume:
				case SecurityChangeTypes.AsksCount:
				case SecurityChangeTypes.AsksVolume:
				case SecurityChangeTypes.OpenInterest:
				{
					value = reader.DeserializeVolume();
					break;
				}
				case SecurityChangeTypes.ImpliedVolatility:
				{
					value = DeserializeChange(reader, metaInfo.ImpliedVolatility);
					break;
				}
				case SecurityChangeTypes.HistoricalVolatility:
				{
					value = DeserializeChange(reader, metaInfo.HistoricalVolatility);
					break;
				}
				case SecurityChangeTypes.TheorPrice:
				{
					value = DeserializeChange(reader, metaInfo.TheorPrice);
					break;
				}
				case SecurityChangeTypes.Delta:
				{
					value = DeserializeChange(reader, metaInfo.Delta);
					break;
				}
				case SecurityChangeTypes.Gamma:
				{
					value = DeserializeChange(reader, metaInfo.Gamma);
					break;
				}
				case SecurityChangeTypes.Vega:
				{
					value = DeserializeChange(reader, metaInfo.Vega);
					break;
				}
				case SecurityChangeTypes.Theta:
				{
					value = DeserializeChange(reader, metaInfo.Theta);
					break;
				}
				case SecurityChangeTypes.MarginBuy:
				{
					value = DeserializeChange(reader, metaInfo.MarginBuy);
					break;
				}
				case SecurityChangeTypes.MarginSell:
				{
					value = DeserializeChange(reader, metaInfo.MarginSell);
					break;
				}
				case SecurityChangeTypes.MinStepSize:
				{
					value = metaInfo.MinStepSize;
					break;
				}
				case SecurityChangeTypes.MinStepPrice:
				{
					value = DeserializeChange(reader, metaInfo.MinStepPrice);
					break;
				}
				case SecurityChangeTypes.LastTrade:
				{
					metaInfo.Price.First = reader.DeserializePrice(metaInfo.Price.First, metaInfo.MinStepSize);

					value = new Trade
					{
						Security = Security,
						Price = metaInfo.Price.First,
						Volume = reader.DeserializeVolume(),
						Time = metaInfo.FirstTime,
						OrderDirection = reader.DeserializeTradeDirection(),
					};
					break;
				}
				case SecurityChangeTypes.BestBid:
				case SecurityChangeTypes.BestAsk:
				{
					metaInfo.Price.First = reader.DeserializePrice(metaInfo.Price.First, metaInfo.MinStepSize);

					value = new Quote
					{
						Security = Security,
						Price = metaInfo.Price.First,
						Volume = reader.DeserializeVolume(),
						OrderDirection = type == SecurityChangeTypes.BestBid ? OrderDirections.Buy : OrderDirections.Sell,
					};
					break;
				}
				default:
					throw new InvalidOperationException("Неизвестное изменение {0} инструмента.".Put(type));
			}

			return new SecurityChange(Security, metaInfo.FirstTime, type, value);
		}

		private void SerializePrice(List<bool> bits, SecurityMetaInfo metaInfo, decimal price)
		{
			if (price == 0)
				throw new ArgumentOutOfRangeException("price");

			var pair = metaInfo.Price;

			if (pair.First == 0)
				pair.First = pair.Second = price;

			bits.SerializePrice(price, pair.Second, metaInfo.MinStepSize, Security);
			pair.Second = price;
		}

		private static void SerializeChange(List<bool> bits, RefPair<decimal, decimal> info, decimal price)
		{
			if (price == 0)
				throw new ArgumentOutOfRangeException("price");

			if (info.First == 0)
				info.First = info.Second = price;

			info.Second = bits.SerializeDecimal(price, info.Second);
		}

		private static decimal DeserializeChange(BitArrayReader reader, RefPair<decimal, decimal> info)
		{
			info.First = reader.DeserializeDecimal(info.First);
			return info.First;
		}
	}
}